/*
 * Copyright (C) 2017 The Guava Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package com.google.common.io;

import com.google.caliper.BeforeExperiment;
import com.google.caliper.Benchmark;
import com.google.caliper.Param;
import com.google.caliper.api.VmOptions;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.nio.CharBuffer;
import java.util.Random;

/**
 * Benchmarks for {@link CharStreams#copy}.
 *
 * <p>{@link CharStreams#copy} has type specific optimizations for various common Appendable and
 * Reader implementations, this compares the performance of the different options.
 */
// These benchmarks allocate a lot of data so use a large heap
@VmOptions({"-Xms12g", "-Xmx12g", "-d64"})
public class CharStreamsCopyBenchmark
{
    enum CopyStrategy
    {
        OLD
                {
                    @Override
                    long copy(Readable from, Appendable to) throws IOException
                    {
                        CharBuffer buf = CharStreams.createBuffer();
                        long total = 0;
                        while (from.read(buf) != -1)
                        {
                            buf.flip();
                            to.append(buf);
                            total += buf.remaining();
                            buf.clear();
                        }
                        return total;
                    }
                },
        NEW
                {
                    @Override
                    long copy(Readable from, Appendable to) throws IOException
                    {
                        return CharStreams.copy(from, to);
                    }
                };

        abstract long copy(Readable from, Appendable to) throws IOException;
    }

    enum TargetSupplier
    {
        STRING_WRITER
                {
                    @Override
                    Appendable get(int sz)
                    {
                        return new StringWriter(sz);
                    }
                },
        STRING_BUILDER
                {
                    @Override
                    Appendable get(int sz)
                    {
                        return new StringBuilder(sz);
                    }
                };

        abstract Appendable get(int sz);
    }

    @Param
    CopyStrategy strategy;
    @Param
    TargetSupplier target;

    @Param({"10", "1024", "1048576"})
    int size;

    String data;

    @BeforeExperiment
    public void setUp()
    {
        // precalculate some random strings of ascii characters.
        StringBuilder sb = new StringBuilder();
        Random random = new Random(0xdeadbeef); // for unpredictable but reproducible behavior
        sb.ensureCapacity(size);
        for (int k = 0; k < size; k++)
        {
            // [9-127) includes all ascii non-control characters
            sb.append((char) (random.nextInt(127 - 9) + 9));
        }
        data = sb.toString();
    }

    @Benchmark
    public long timeCopy(int reps) throws IOException
    {
        long r = 0;
        final String localData = data;
        final TargetSupplier localTarget = target;
        final CopyStrategy localStrategy = strategy;
        for (int i = 0; i < reps; i++)
        {
            Appendable appendable = localTarget.get(localData.length());
            r += localStrategy.copy(new StringReader(localData), appendable);
        }
        return r;
    }
}
